package org.gc.amino.universe;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

import org.gc.amino.engine.mote.Mote;
import org.gc.amino.engine.terrainmap.PointD;
import org.gc.amino.ia.MoteSimulator;
import org.gc.amino.ia.scalar.ScalarField;
import org.gc.amino.ia.scalar.ScalarFieldSourcePotential;
public class Universe {
	public static int MAX_QUADTREE_DEPTH = 4;
	private List<MoteSimulator> motes;
	private MoteSimulator me;
	private ScalarField scalar_field;
	private QuadTree quadtree;
	private PointD terrain_size;
	
	public Universe(PointD terrain_size, List<Mote> motes, Mote you) {
		this.motes = new ArrayList<MoteSimulator>();
		
		for(Mote mote : motes) {
			this.motes.add(new MoteSimulator(mote));
		}
		this.me = new MoteSimulator(you);
		this.motes.add(this.me);
		
		this.scalar_field = new ScalarField(this.motes);
		
		this.terrain_size = terrain_size;
		this.quadtree = new QuadTree.Root(MAX_QUADTREE_DEPTH, new Rectangle(0.0, terrain_size.x, 0.0, terrain_size.y));
	}
	
	public Universe(List<MoteSimulator> motes, PointD terrain_size, MoteSimulator me) {
		this(motes, terrain_size, new Rectangle(0.0, terrain_size.x, 0.0, terrain_size.y), me);
	}
	
	public Universe(List<MoteSimulator> motes, PointD terrain_size, Rectangle rect, MoteSimulator me) {
		this.motes = motes;
		this.me = me;
		this.scalar_field = new ScalarField(this.motes);
		this.terrain_size = terrain_size;
		this.quadtree = new QuadTree.Root(MAX_QUADTREE_DEPTH, rect);
	}

	public Universe copy() {
		List<MoteSimulator> new_motes = new ArrayList<MoteSimulator>();
		MoteSimulator new_me = new MoteSimulator(this.me);
		
		for(MoteSimulator m : this.motes) {
			new_motes.add(new MoteSimulator(m));
		}
		
		PointD new_terrain_size = this.terrain_size;
		
		return new Universe(new_motes, new_terrain_size, new_me);
	}
	
	public MoteSimulator getMe() {
		return me;
	}
	
	public void addFurnitureMote(PointD position, PointD velocity, float radius) {
		MoteSimulator mote = new MoteSimulator("furniture", position, velocity, radius);
		this.motes.add(mote);
	}	
	
	public Universe simulateStep(float time_step) {
		Universe next = new Universe(this.motes, this.terrain_size, this.quadtree.getRect(), this.me);
		next.update(time_step);
		next.collide();
		
		return next;
	}
	
	public void collide() {
		this.quadtree = this.quadtree.addList(this.motes);
		for(MoteSimulator mote : this.motes) {
			mote.collideAgainstCandidates(this.quadtree.getPossibleColliders(mote));
		}
	}
	
	
	
	public void update(float time_step) {
		for(MoteSimulator mote : this.motes) {
			mote.update(time_step, this.terrain_size);
		}
	}
	
	public float computeFieldPotential(float ref_radius, Point2D.Float point, ScalarFieldSourcePotential excluded, int courage) {
		return this.scalar_field.computeFieldPotential(ref_radius, point, excluded, courage);
	}
}
